-- ActivityDungeonM
-- Created by chengb Aug/25/2015
-- 活动迷宫（活动副本）

module("ActivityDungeonM", package.seeall);

local PURE_FILE = "ActivityDungeonM";

-- 配置表
local activityDungeonTable = {};
local activityConfigList   = {};

-- 内部函数声明
local whenEnterUnreal;
local whenMonsterClear;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 载入活动迷宫配置表
    activityDungeonTable = {};
    activityConfigList = {};
    local activity_dungeon_CSV = get_activity_dungeon_CSV();
    for i = 1, #activity_dungeon_CSV do
        -- 先插入活动列表
        table.insert(activityConfigList, activity_dungeon_CSV[i]);

        -- 根据id和难度存放配置
        local id    = activity_dungeon_CSV[i].id;
        local grade = activity_dungeon_CSV[i].grade;
        if not activityDungeonTable[id] then
            activityDungeonTable[id] = {};
        end
        activityDungeonTable[id][grade] = activity_dungeon_CSV[i];
    end
    activity_dungeon_CSV = nil;
end

-- 模块初始化操作
function init()
    loadCsv();

    EventMgr.removeAll("ActivityM");

    -- 关注进入夹层的事件
    EventMgr.register("ActivityM", event.ENTER_UNREAL_DUNGEON, whenEnterUnreal);

    -- 关注清怪的事件
    EventMgr.register("ActivityM", event.MONSTER_CLEAR, whenMonsterClear);
end

-- 检索活动配置
function query(id, grade, path)
    -- 默认查询简单难度
    grade = grade or ACTIVITY_GRADE_EASY;
    if not activityDungeonTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return table.deepcopy(activityDungeonTable[id][grade]);
    end

    return table.deepcopy(activityDungeonTable[id][grade][path]);
end

-- 根据id获取活动
function getActivitiesById(id)
    local list = activityDungeonTable[id];
    if not list then
        return {};
    end

    local ret = {};
    for _, info in pairs(list) do
        table.insert(ret, info);
    end

    -- 按照难度升序
    table.sort(ret, function(a, b) return a.grade < b.grade; end);

    return ret;
end

-- 根据id获取当前迷宫是否全部通关
function isAllPassById(id)
    local list = getActivitiesById(id);
    for _, info in pairs(list) do
        local dungeonId = info["dungeon_id"];
        if not DungeonAreaM.isDungeonPassed(ME.user, dungeonId) then
            -- 有某个没有通关
            return false;
        end
    end
    return true;
end

-- 根据id获取当前迷宫是否有可领取奖励
function hasBonusToTake(id)
    local list = getActivitiesById(id);
    for _, info in pairs(list) do
        local dungeonId = info["dungeon_id"];
        if DungeonAreaM.isDungeonPassed(ME.user, dungeonId) and
            not DungeonAreaM.isDungeonPassBonusTaken(ME.user, dungeonId) then
            -- 有某个通关了但是没领奖
            return true;
        end
    end
    return false;
end

-- 根据id获取当前迷宫是否通关某难度
function isGradePassById(id, grade)
    local list = getActivitiesById(id);

    local info =  list[grade];
    local dungeonId = info["dungeon_id"];

    if not DungeonAreaM.isDungeonPassed(ME.user, dungeonId) then
        -- 没有通关
        return false;
    end

    return true;
end

-- 根据id获取主迷宫
function getMainDungeonById(id)
    local list = getActivitiesById(id);
    for _, info in pairs(list) do
        local dungeonId = info["dungeon_id"];
        return DungeonAreaM.getParentId(dungeonId);
    end
end

-- 根据迷宫id获取对应的活动配置
function getActivityByDungeonId(dungeonId)
    for _, info in pairs(activityConfigList) do
        if info["dungeon_id"] == dungeonId then
            return info;
        end
    end
    return nil;
end

-- 是否是活动迷宫
function isActivityDungeon(dungeonId)
    local dungeonType = DungeonAreaM.query(dungeonId, "type");
    return dungeonType == "activity";
end

-- 是否在活动迷宫中
function isInActivityDungeon()
    if not DungeonM.isInDungeon() then
        -- 不在迷宫中
        return false;
    end

    -- 判断是否是活动迷宫
    local dungeonId = DungeonM.getDungeonId();
    return isActivityDungeon(dungeonId);
end

-- 是否在活动迷宫BOSS层
function isInActivityBossLayer()
    if not DungeonM.isInDungeon() then
        -- 不在迷宫中
        return false;
    end

    -- 判断是否是活动迷宫
    local dungeonId = DungeonM.getDungeonId();
    local layer     = DungeonM.currentLayer();

    if not isActivityDungeon(dungeonId) then
        -- 不是活动副本
        return false;
    end

    if UnrealDungeonM.isInUnreal() then
        -- 夹层
        local unrealId = UnrealDungeonM.getUnrealId();
        local dbase = UnrealDungeonM.query(unrealId, "dbase");
        -- 指定的夹层
        return dbase["activity_boss"] == 1;
    end

    if DungeonBossM.isBossLayer(dungeonId, layer) then
        -- boss层
        return true;
    end

    return false;
end

-------------------------
-- 判断能否进入活动迷宫
-- @param id    活动id
-- @param grade 难度
function canStartActivityDungeon(id, grade)
    if type(id) ~= 'number' or type(grade) ~= "number" then
        -- 参数不合法
        return false;
    end

    local activityCfg = query(id, grade);
    if type(activityCfg) ~= 'table' then
        trace(PURE_FILE, "获取不到(%d/%d)的活动配置", id, grade);
        return false;
    end

    if not isActivityOpen() then
        trace(PURE_FILE, "玩家尚未完成前置条件");
        return false;
    end

    return true;
end

-- 获取正在进行中活动副本
function getCurrentActivityId()
    local currentActivities = ActivityM.getCurrentActivities();

    for id, info in pairs(currentActivities) do
        local status = ActivityM.getActivityStatus(ActivityM.getActivityInfo(id));
        -- 返回第一个正在进行的活动副本
        if query(id) and ActivityM.query(id, "rule") == "dungeon"
            and (status == ACTIVITY_STATUS_START or status == ACTIVITY_STATUS_END) then
                return id;
        end
    end
    return 0;
end

-- 计算怪物掉落
function calcDrop(monster)
    local classId = monster.classId;
    if monster:isSlime() or monster:isBoss() then
        -- BOSS怪
        local bonus = ActivityBossM.queryBoss(classId, "drop_bonus");
        if type(bonus) == "table" and #bonus > 0 then
            -- 配置了固定掉落
            return bonus;
        elseif type(bonus) == "string" then
            -- 配置了公式计算
            local args = ActivityBossM.queryBoss(classId, "dbase");
            return FormulaM.invoke(bonus, ME.user, args);
        end
    end

    return nil;
end

-- 特殊的道具位置
function specialItemOffset(classId)
    -- TODO想不到啥好办法，现在这样吧
    if classId == 7408 then
        -- 机械神心脏需要下移位置
        return {0, -40};
    end
    return nil;
end

-- 有奖励未领取
function haveBonus()
    -- 获取所有格子
    local curDungeon = DungeonM.getCurrentDungeon();
    for _, grid in pairs(curDungeon) do
        if grid:isOpened() and grid.bonus and
            (not grid.monster or grid.monster:isDead()) then
            return true;
        end
    end

    return false;
end

-- 帮玩家拾取
function autoPickItem()
    for pos = 1, GRID_SIZE do
        local grid = DungeonM.getGridByPos(pos);

        -- 是否可拾取
        if (grid.type == GRID_TYPE_MONSTER or
            grid.type == GRID_TYPE_BOSS or
            grid.type == GRID_TYPE_ITEM or
            grid.type == GRID_TYPE_BOX or
            grid.type == GRID_TYPE_SLIME or
            grid.type == GRID_TYPE_APPRENTICE or
            grid.type == GRID_TYPE_MAP or
            grid.type == GRID_TYPE_BOSS_BOX or
            grid.type == GRID_TYPE_ACTIVITY_BOSS_BOX or
            grid.type == GRID_TYPE_SUMMON) and
            grid:isOpened() and grid.bonus and
            (not grid.monster or grid.monster:isDead()) then
            -- 物品
            if not grid.display or DungeonServiceM.isMonsterClear() then
                DungeonActionM.go("pick_item", pos);
            end
        end
    end
end

-- 是否需要隐藏底部格子
function needHideFrame()
    if not isInActivityBossLayer() then
        return false;
    end

    if UnrealDungeonM.isInUnreal() then
        local unrealId = UnrealDungeonM.getUnrealId();
        local dbase = UnrealDungeonM.query(unrealId, "dbase");
        return dbase["disframe"] == 1;
    end

    local dungeonId = DungeonM.getDungeonId();
    local info = getActivityByDungeonId(dungeonId);
    local dbase = info["dbase"] or {};

    return dbase["disframe"] == 1;
end

-- 活动副本是否开启
function isActivityOpen()
    -- 通关第1-3
    return DungeonAreaM.isDungeonPassed(ME.user, 13);
end

-- 计算活动宝箱
function calcActivityBoxBonus(args)
    local ret = table.deepcopy(args);
    local bonus = ret["bonus"];
    if type(bonus) == "string" then
        -- 配置了公式计算
        bonus = FormulaM.invoke(bonus, ME.user) or {};
        ret["bonus"] = bonus;
    end

    return ret;
end

-- 玩家进入夹层的回调
function whenEnterUnreal(args)
    if not isInActivityDungeon() then
        -- 不在活动迷宫中
        return;
    end

    local unrealId = args.unrealId;
    local dungeonId = DungeonM.getDungeonId();
    local activityId = getActivityByDungeonId(dungeonId)["id"];
    local dbase = query(activityId, ACTIVITY_GRADE_EASY, "dbase");
    if type(dbase["unreal_dungeon_stat"]) ~= "table" or
       table.indexOf(dbase["unreal_dungeon_stat"], unrealId) == -1 then
        -- 该活动不需要统计夹层
        return;
    end

    -- 统计活动期间进入夹层的次数
    ActivityM.statEnterUreal(activityId, unrealId);
end

-- 关注清怪的事件
function whenMonsterClear(args)
    -- 活动BOOS层中，怪物全部死了门打开
    if ActivityDungeonM.isInActivityBossLayer() then
        DungeonM.openDoor();
    end
end


